package gpl.scotlandyard.services.basics;

import gpl.scotlandyard.beans.basics.Link;
import gpl.scotlandyard.beans.basics.Node;
import gpl.scotlandyard.beans.basics.Player;
import gpl.scotlandyard.beans.basics.Ticket;
import gpl.scotlandyard.exceptions.IllegalMoveException;
import gpl.scotlandyard.utils.Config;

import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Random;

import org.apache.log4j.Logger;

/** @author norbert */
public class BoardManagerBuilder {

  private static final Logger LOG = Logger.getLogger(BoardManagerBuilder.class);
  private BoardManager manager = new BoardManager();

  /** @param detective */
  public void addDetective(Player detective) {
    manager.getBoard().addDetective(detective);
    LOG.info("detective added");
  }

  /** @param player */
  public void addFugitive(Player player) {
    if (!player.isFugitive()) {
      throw new IllegalArgumentException("player must be a fugitive : player.isFugitive() ");
    }
    manager.getBoard().setFugitive(player);
    LOG.info("fugitive added");
  }

  /** Initializes player nodes. */
  public void affectRandomPositions() {
    Node node;
    boolean isPositionned;
    for (Player detective : manager.getBoard().getDetectives()) {
      isPositionned = false;
      while (!isPositionned) {
        node = getFreeRandomNode();
        try {
          manager.getPlayerManager().move(detective, Link.buildStart(node));
          isPositionned = true;
          LOG.info(detective.getName() + " positionned " + detective.getNode());
        } catch (IllegalMoveException e) {
          LOG.warn("reject start position : " + e.getLink());
          isPositionned = false;
        }
      }
    }

    boolean isHold = true;
    isPositionned = false;
    while (isHold || !isPositionned) {
      node = getFreeRandomNode();
      try {
        manager.getPlayerManager().move(manager.getBoard().getFugitive(), new Link(Node.getUnknownNode(), node, Ticket.START));
        isPositionned = true;
        LOG.info(manager.getBoard().getFugitive().getName() + " positionned but it's a secret");
        isHold = manager.isFugitiveHold();
      } catch (IllegalMoveException e) {
        isPositionned = false;
      }
    }
    LOG.info("Player positions initialized");
  }

  /** Initializes tickets and affects random positions.
   * @return built manager */
  public BoardManager getRandomBoardManager() {
    initializePlayersTickets();
    affectRandomPositions();
    return manager;
  }

  /** Returns the board manager as it was built.
   * @return built manager
   */
  public BoardManager getBoardManager() {
    return manager;
  }

  /** Looks in configuration file.*/
  public void initializePlayersTickets() {
    int taxi = Config.getInteger("D_TAXI");
    int bus = Config.getInteger("D_BUS");
    int ug = Config.getInteger("D_UG");
    int hide = Config.getInteger("D_HIDE");
    int twice = Config.getInteger("D_TWICE");

    for (Player player : manager.getBoard().getDetectives()) {
      player.setTaxi(taxi);
      player.setBus(bus);
      player.setUg(ug);
      player.setHide(hide);
      player.setTwice(twice);
    }

    taxi = Config.getInteger("F_TAXI");
    bus = Config.getInteger("F_BUS");
    ug = Config.getInteger("F_UG");
    hide = manager.getBoard().getDetectives().size();
    twice = Config.getInteger("F_TWICE");

    Player player = manager.getBoard().getFugitive();
    player.setTaxi(taxi);
    player.setBus(bus);
    player.setUg(ug);
    player.setHide(hide);
    player.setTwice(twice);

    LOG.info("tickets initialized");

    manager.getBoard().setMaxTurn(Config.getInteger("MAX_TURN"));
    LOG.info("max turn : " + manager.getBoard().getMaxTurn());
  }

  /** @param map */
  public void setMap(List<Node> map) {
    Collections.sort(map);
    manager.getBoard().setMap(map);
  }

  /** Returns a free random node.
   * @return free node. */
  private Node getFreeRandomNode() {
    Random rand = new Random(new Date().getTime());
    Node free = null;
    int size = manager.getBoard().getMap().size();
    if (size == 0) {
      throw new IllegalStateException("map not loaded");
    }
    while (free == null) {
      int index = rand.nextInt(size);
      free = manager.getBoard().getMap().get(index);
      if (manager.getBoard().getFugitive() != null && free.equals(manager.getBoard().getFugitive().getNode())) {
        free = null;
        continue;
      }
      for (Player detective : manager.getBoard().getDetectives()) {
        if (free.equals(detective.getNode())) {
          free = null;
          break;
        }
      }
    }
    return free;
  }

  /**
   * @param playerManager
   */
  public void setPlayerManager(PlayerManager playerManager) {
    manager.setPlayerManager(playerManager);
  }
}
